package com.reggie.service.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.reggie.acl.dataobject.Category;
import com.reggie.acl.dataobject.Setmeal;
import com.reggie.acl.dataobject.SetmealDish;
import com.reggie.acl.mapper.SetmealMapper;
import com.reggie.common.enums.ResponseCode;
import com.reggie.common.exception.ReggieException;
import com.reggie.common.util.AssertUtils;
import com.reggie.service.model.bo.SetmealBO;
import com.reggie.service.model.request.SetmealPageRequest;
import com.reggie.service.service.CategoryService;
import com.reggie.service.service.SetmealDishService;
import com.reggie.service.service.SetmealService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * <p>
 * 套餐 服务实现类
 * </p>
 *
 * @author yth
 * @since 2023-02-04
 */
@Slf4j
@Service
public class SetmealServiceImpl implements SetmealService {

    @Autowired
    private SetmealMapper setmealMapper;

    @Autowired
    private CategoryService categoryService;

    @Autowired
    private SetmealDishService setmealDishService;

    /**
     * 给dto添加categoryName属性
     *
     * @param setmealBO dto
     */
    private void addCategoryName(final SetmealBO setmealBO) {
        Long categoryId = setmealBO.getCategoryId();
        categoryService.getById(categoryId)
                .ifPresent(e -> setmealBO.setCategoryName(e.getName()));
    }

    private void addCategoryName(final List<SetmealBO> setmealBOList) {
        Set<Long> set = setmealBOList.stream()
                .map(SetmealBO::getCategoryId)
                .collect(Collectors.toSet());
        List<Category> categories = categoryService.getByIds(set);
        Map<Long, String> map = categories.stream()
                .collect(Collectors.toMap(Category::getId, Category::getName));
        setmealBOList.forEach(e -> e.setCategoryName(map.get(e.getCategoryId())));
    }

    /**
     * 给dto添加flavors属性
     *
     * @param setmealBO dto
     */
    private void addDishes(final SetmealBO setmealBO) {
        Long setmealId = setmealBO.getId();
        List<SetmealDish> dishes = setmealDishService.getBySetmealId(setmealId);
        setmealBO.setSetmealDishes(dishes);
    }

    private void addDishes(final List<SetmealBO> setmealBOList) {
        Set<Long> set = setmealBOList.stream()
                .map(SetmealBO::getId)
                .collect(Collectors.toSet());
        List<SetmealDish> dishes = setmealDishService.getBySetmealIds(set);
        Map<Long, List<SetmealDish>> map = dishes.stream()
                .collect(Collectors.groupingBy(SetmealDish::getSetmealId));
        setmealBOList.forEach(e -> {
                    List<SetmealDish> setmealDishes = map.getOrDefault(e.getId(), new ArrayList<>(8));
                    e.setSetmealDishes(setmealDishes);
                }
        );
    }

    @Override
    public int countByCategoryId(long categoryId) {
        LambdaQueryWrapper<Setmeal> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Setmeal::getCategoryId, categoryId);
        return setmealMapper.selectCount(wrapper);
    }

    @Override
    @Transactional
    public IPage<SetmealBO> page(SetmealPageRequest request) {
        AssertUtils.nonNull(request, ResponseCode.CLIENT_ERROR);
        AssertUtils.nonNull(request.getPage(), ResponseCode.CLIENT_ERROR);
        AssertUtils.nonNull(request.getPageSize(), ResponseCode.CLIENT_ERROR);

        final String name = request.getName();
        final Integer page = request.getPage();
        final Integer pageSize = request.getPageSize();
        LambdaQueryWrapper<Setmeal> wrapper = Wrappers.lambdaQuery(Setmeal.class)
                .like(StringUtils.isNotBlank(name), Setmeal::getName, name)
                .orderByDesc(Setmeal::getUpdateTime);
        IPage<Setmeal> entityPage = setmealMapper.selectPage(new Page<>(page, pageSize), wrapper);
        IPage<SetmealBO> dtoPage = entityPage.convert(SetmealBO::new);
        if (CollectionUtils.isNotEmpty(dtoPage.getRecords())) {
            addCategoryName(dtoPage.getRecords());
        }
        return dtoPage;
    }

    @Override
    public void updateStatusBatchIds(int status, List<Long> ids) {
        LambdaUpdateWrapper<Setmeal> wrapper = Wrappers.lambdaUpdate(Setmeal.class);
        wrapper.in(Setmeal::getId, ids)
                .set(Setmeal::getStatus, status);
        setmealMapper.update(new Setmeal(), wrapper);
    }

    @Override
    public void removeBatchIds(List<Long> ids) {
        // 查询其中“在售”的套餐数量
        LambdaQueryWrapper<Setmeal> wrapper = new LambdaQueryWrapper<>();
        wrapper.in(Setmeal::getId, ids).eq(Setmeal::getStatus, 1);
        int count = setmealMapper.selectCount(wrapper);
        if (count > 0) {
            throw new ReggieException(ResponseCode.CLIENT_ERROR, "只能删除“停售”的套餐");
        }
        // 删除套餐与菜品的关联数据
        setmealDishService.removeBatchSetmealIds(ids);
        // 删除套餐
        setmealMapper.deleteBatchIds(ids);
    }

    @Override
    @Transactional
    public void saveWithDishes(SetmealBO setmealBO) {
        // 保存套餐
        setmealMapper.insert(setmealBO);
        // 给套餐关联菜品添加套餐id、sort字段
        List<SetmealDish> setmealDishes = setmealBO.getSetmealDishes();
        Long setmealId = setmealBO.getId();
        for (int i = 0; i < setmealDishes.size(); i++) {
            SetmealDish setmealDish = setmealDishes.get(i);
            setmealDish.setSetmealId(setmealId);
            setmealDish.setSort(i + 1);
        }
        // 批量保存套餐菜品
        setmealDishService.save(setmealDishes);
    }

    @Override
    @Transactional
    public Optional<SetmealBO> getWithDishById(long id) {
        Setmeal setmeal = setmealMapper.selectById(id);
        return Optional.ofNullable(setmeal)
                .map(SetmealBO::new)
                .map(e -> {
                    addCategoryName(e);
                    addDishes(e);
                    return e;
                });
    }

    @Override
    @Transactional
    public void update(SetmealBO setmealBO) {
        // 保存套餐
        if (setmealMapper.updateById(setmealBO) <= 0) {
            log.error("数据库服务套餐修改失败,{}", setmealBO);
            throw new ReggieException(ResponseCode.SERVER_ERROR, "数据库服务套餐修改失败");
        }
        // 删除关联的套餐菜品数据
        List<Long> collect = Stream.of(setmealBO.getId()).collect(Collectors.toList());
        setmealDishService.removeBatchSetmealIds(collect);
        // 给套餐关联菜品添加套餐id、sort字段
        List<SetmealDish> setmealDishes = setmealBO.getSetmealDishes();
        Long setmealId = setmealBO.getId();
        for (int i = 0; i < setmealDishes.size(); i++) {
            SetmealDish setmealDish = setmealDishes.get(i);
            setmealDish.setSetmealId(setmealId);
            setmealDish.setSort(i + 1);
        }
        // 批量保存套餐菜品
        setmealDishService.save(setmealDishes);
    }

    @Override
    @Transactional
    public List<SetmealBO> listWithDish(Setmeal setmeal) {
        Long categoryId = setmeal.getCategoryId();
        Integer status = setmeal.getStatus();
        LambdaQueryWrapper<Setmeal> wrapper = Wrappers.lambdaQuery(Setmeal.class)
                .eq(categoryId != null, Setmeal::getCategoryId, categoryId)
                .eq(status != null, Setmeal::getStatus, status)
                .orderByDesc(Setmeal::getUpdateTime);
        List<Setmeal> setmealList = setmealMapper.selectList(wrapper);
        List<SetmealBO> setmealBOList = setmealList.stream()
                .map(SetmealBO::new)
                .collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(setmealBOList)) {
            addDishes(setmealBOList);
        }
        return setmealBOList;
    }

    @Override
    public Optional<Setmeal> getById(long setmealId, Integer status) {
        LambdaQueryWrapper<Setmeal> wrapper = Wrappers.lambdaQuery(Setmeal.class)
                .eq(Setmeal::getId, setmealId)
                .eq(status != null, Setmeal::getStatus, status);
        return Optional.ofNullable(setmealMapper.selectOne(wrapper));
    }
}
